#pragma once



#include <metalc/base/mlayer.h>

#include <dbg/cycles.h>

#define SAMPLING_CNT	(8)
#define MIN_REPORT_NR	(1024)
#define WCET_PRINT(...)	printf(__VA_ARGS__)
/* 暂时 */
#define WCET_PERM_ALLOC	(page_alloc)

#ifdef HDEF_CPU_MODEL_CORE_13700K
	#define STEP 6144
	#define UPPER_LIMIT (3 * 1024 * 1024)
#elif defined(HDEF_CPU_MODEL_RYZEN_3700X)
	#define STEP 6144
	#define UPPER_LIMIT (3 * 1024 * 1024)
#elif defined(HDEF_CPU_MODEL_SM8350)
	#define STEP 32
	#define UPPER_LIMIT 16384
#endif

#define MAX_SNR (UPPER_LIMIT / STEP)
#define SNR_DISPLAY_LIMIT (100)

// static word_t snr_arr_alc[MAX_SNR] = {0};
// static word_t snr_arr_dalc[MAX_SNR] = {0};
#define CNTTYPE	 hword_t
typedef struct wcet_obj
{
	word_t alloc_wcet_cycles;
	word_t dealloc_wcet_cycles;

	word_t alloc_cnt;
	word_t dealloc_cnt;

	//double cycles_per_nanoseconds;
	// word_t cpu_freq;
	CNTTYPE sample_alloc_slots[MAX_SNR];

	CNTTYPE sample_dealloc_slots[MAX_SNR];
	
	struct wcet_obj *next;
} wcet_obj_t;

DEF_MLAYER_BASIC(wcet, -1, 1)
// static const size_t wcet_objnr = 1;
static wcet_obj_t *wcethd;
SAI wcet_obj_t *wcet_init(wcet_obj_t *l, double cpu_freq)
{
	PARAM_USED(cpu_freq);

	__memset(l, 0, sizeof(wcet_obj_t));
	
	l->alloc_wcet_cycles = 0;
	l->dealloc_wcet_cycles = 0;
	l->alloc_cnt = 0;
	l->dealloc_cnt = 0;
	// l->cpu_freq = cpu_freq;
	//cyclestamp_measure();
	// l->sample_alloc_slots = (word_t *)((word_t)l + sizeof(wcet_obj_t));
	// l->sample_dealloc_slots = (word_t *)((word_t)l->sample_alloc_slots + sizeof(word_t) * l->sample_alloc_max);

	/* 挂入全局链表 */
	
	wcet_obj_t *cur = swp_rel(&wcethd, l);
	l->next = cur;

	return l;
}

SAI wcet_obj_t *wcet_create(perm_allocf_t *palloc, word_t sampling_slots_nr, double cpu_freq)
{
	PARAM_USED(sampling_slots_nr);
	/* 太大了申请不下 */
	// wcet_obj_t *ret = (wcet_obj_t *)mlcc_palloc(sizeof(wcet_obj_t));
	wcet_obj_t *ret = (wcet_obj_t *)palloc(sizeof(wcet_obj_t));
	if (!ret)
		return NULL;
	return wcet_init(ret, cpu_freq);
}

SAI void wcet_alloc_record(wcet_obj_t *l, word_t wcet_cycles)
{
	word_t idx = wcet_cycles >> log2_align(STEP);
	if (idx >= MAX_SNR)
		idx = MAX_SNR - 1;
	l->sample_alloc_slots[idx]++;
}
SAI void wcet_dealloc_record(wcet_obj_t *l, word_t wcet_cycles)
{
	word_t idx = wcet_cycles >> log2_align(STEP);
	if (idx >= MAX_SNR)
		idx = MAX_SNR - 1;
	l->sample_dealloc_slots[idx]++;
}

SAI void wcet_pinit(const mlfunc_t * cont){
	PARAM_USED(cont);
	/* WCET_PRINT("wcet pinit\n"); */
}
SAI void _wcet_tinit(const mlfunc_t * cont, perm_allocf_t *palloc){
	
	/* WCET_PRINT("wcet t init pre\n"); */
	wcet_obj_t *l = wcet_create(palloc, 0, HDEF_CYCLE_FREQ_PERNS);
	// ASSUME(l);
	// if (!l) fatal("wcet allocated failed");
	ASSERT(l);
	/* ERR: 这里陷入malloc */
	mtls_t *tls = mtls_get();
	if (!tls->objs[cont->lidx]) tls->objs[cont->lidx] = l;
	/* WCET_PRINT("wcet t init ok\n"); */
}
SAI void wcet_tinit(const mlfunc_t * cont){
	_wcet_tinit(cont, WCET_PERM_ALLOC);
}
SAI void *wcet_alloc(mreq_t *req, const mlfunc_t * cont){
	void *ret;
	word_t stmp;
	bool_t flag = 0;

	//WCET_PRINT("[m]%lu\n", req->size);
	

	mtls_t *tls = mtls_by_req(req);
	/*if (!tls) {
		WCET_PRINT("try get tls\n");
		mtls_t **ptls = mtls_getp();
		WCET_PRINT("get ptls %p, %p\n", ptls, *ptls);
		tls = *ptls;
		if (!tls) fatal ("notls?\n");
		req->tls = tls;
	}*/
	wcet_obj_t *l = (wcet_obj_t *)mtls_lobj(tls, cont->lidx);
	ASSERT(l);
	if (hash(l->alloc_cnt++) % SAMPLING_CNT == 0)
	{
		flag = 1;
	}
	
	if (UNLIKELY(flag))
	{
		cmb();
		stmp = cyclestamp_get();
		ret = cont->lalloc(req);
		stmp = cyclestamp_diff(stmp);
		cmb();
		wcet_alloc_record(l, stmp);
	}
	else
		ret = cont->lalloc(req);
	return ret;
}
SAI bool_t wcet_free(mreq_t *req, const mlfunc_t * cont){
	
	mtls_t *tls = mtls_by_req(req);
	/*if (!tls) {
		tls = mtls_get();
		if (!tls) fatal ("notls?\n");
		req->tls = tls;
	}*/
	wcet_obj_t *l = (wcet_obj_t *)mtls_lobj(tls, cont->lidx);
	word_t stmp;
	bool_t flag = 0;
	bool_t ret;
	ASSERT(l);

	if (hash(l->dealloc_cnt++) % SAMPLING_CNT == 0)
	{
		flag = 1;
	}

	if (UNLIKELY(flag))
	{
		cmb();
		stmp = cyclestamp_get();
		ret = cont->lfree(req);
		stmp = cyclestamp_diff(stmp);
		cmb();

		wcet_dealloc_record(l, stmp);
	}
	else
		ret = cont->lfree(req);

	return ret;
}
SAI size_t wcet_sizeof(mreq_t *req, const mlfunc_t * cont){
	return cont->lsizeof(req);
}


SAI word_t calculate_sum(CNTTYPE *arr, word_t nr)
{
	word_t sum = 0;
	for (word_t i = 0; i < nr; i++)
	{
		sum += arr[i];
	}
	return sum;
}
SAI word_t calculate_average(CNTTYPE *arr, word_t nr)
{
	word_t sum = calculate_sum(arr, nr);
	return (word_t)(sum / nr);
}

// size分布
SAI void arr_findmax_min(CNTTYPE *arr, word_t nr, word_t *max, word_t *min)
{
	if (!arr)
		return;
	for (word_t i = 0; i < nr; i++)
	{
		if (arr[i] > *max)
			*max = arr[i];
		if (arr[i] < *min)
			*min = arr[i];
	}
}

SAI void wcet_print(CNTTYPE *snr_arr, word_t step, word_t snr)
{

	PARAM_USED(snr_arr, step, snr);
	for (word_t j = 0; j < snr; j++)
	{
		// if (snr_arr[j] > SNR_DISPLAY_LIMIT)
		// 	printf("[%8lu] %10u\n", (j + 1) * step, snr_arr[j]);
	}
}
SAI void wcet_distribute(CNTTYPE *arr, word_t nr, word_t step, word_t upper_limit)
{
	// calculate max and min
	word_t max;
	word_t min;
	arr_findmax_min(arr, nr, &max, &min);
	// word_t step = max / display_lines;
	if (min >= max)
		return;
	if (max > upper_limit)
		max = upper_limit;
	word_t snr = max / step;
	if (snr == 0)
		snr = 1;
	// find snr
	word_t *snr_arr = (word_t *)page_alloc(snr * sizeof(word_t));
	__memset(snr_arr, 0, snr * sizeof(word_t));
	word_t snr_idx;
	for (word_t i = 0; i < nr; i++)
	{
		snr_idx = arr[i] / step;

		if (snr_idx >= snr)
			snr_idx = snr - 1;
		snr_arr[snr_idx] += 1;
	}

	//wcet_print(snr_arr, step, snr);

	page_free(snr_arr, snr * sizeof(word_t));
}

SAI void wcet_distribute_sum(CNTTYPE *snr_arr, word_t *arr, word_t nr, word_t step, word_t upper_limit)
{
	// calculate max and min
	word_t max;
	word_t min;

	word_t snr = upper_limit / step;
	if (snr > MAX_SNR)
	{
		snr = MAX_SNR;
		step = upper_limit / snr;
	}
	if (snr == 0)
		snr = 1;
	// find snr
	//word_t *snr_arr = (word_t *)page_alloc(snr * sizeof(word_t));
	//__builtin_memset(snr_arr, 0, snr * sizeof(word_t));
	word_t snr_idx;
	for (word_t i = 0; i < nr; i++)
	{
		snr_idx = arr[i] / step;

		if (snr_idx >= snr)
			snr_idx = snr - 1;
		snr_arr[snr_idx] += 1;
	}

	//os_free(snr_arr, snr * sizeof(word_t));
}

SAI void find_wcet_percentile(CNTTYPE *snr_arr, double *percentile_arr, word_t step, word_t snr, word_t perarr_nr)
{
	// 先求和
	word_t sum = 0;
	for (word_t i = 0; i < snr; i++)
		sum += snr_arr[i];
	// 再倒过来找percentile
	word_t j = snr - 1;
	word_t tsum = 0;
	double nrs;

	// for (word_t i = 0; i < perarr_nr; i++){
	//	 WCET_PRINT("%g\t", percentile_arr[i]);
	// }
	// WCET_PRINT("\n");
	for (word_t i = 0; i < perarr_nr; i++)
	{
		nrs = sum * (1 - percentile_arr[i]);
		while (tsum < nrs)
		{
			tsum += snr_arr[j--];
		}
		// 赋值
		if(i != 0 )WCET_PRINT(" ");
		//percentile_arr[i] = (j+1+1) * step;
		// WCET_PRINT("%lu\t", (j + 1 + 1) * step);
		WCET_PRINT("%.0f", ((j + 1) * step + (j + 1 + 1) * step) / (2 * HDEF_CYCLE_FREQ_PERNS));
	}

	WCET_PRINT("\n");
}



static double percentiles[] = {0.999999, 0.99999, 0.9999, 0.999, 0.99, 0.9};
// {0.9, 0.95, 0.98, 0.99, 0.999, 0.9999, 0.99999};
SAI void sum_arr(CNTTYPE *arr1, CNTTYPE *arr2, word_t nr)
{
	for (word_t i = 0; i < nr; i++)
	{
		arr1[i] += arr2[i];
	}
}


// 
SAI void wcet_stat(wcet_obj_t *hd, wcet_obj_t *l)
{
	static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
	if (l->alloc_cnt < MIN_REPORT_NR && l->dealloc_cnt < MIN_REPORT_NR)
	{
		// fprintf(stderr, "wcet stat\n");
		return;
	}
	while (0 != pthread_mutex_lock(&mtx))
	{
	}
	// word_t avg_alloc = calculate_average(l->sample_alloc_slots, l->sample_alloc_idx);
	// word_t avg_dealloc = calculate_average(l->sample_dealloc_slots, l->sample_dealloc_idx);
	// printf("[wcet_stat] total_alloc: %lu, total_dealloc %lu\n", l->alloc_cnt, l->dealloc_cnt);
	// printf("[wcet_stat] cpu_freq: %luMhz, alloc_wcet: %.1fus, dealloc_wcet: %.1fus\n", l->cpu_freq / FREQ_1M, (double)l->alloc_wcet_cycles * FREQ_1M / l->cpu_freq, (double)l->dealloc_wcet_cycles * FREQ_1M / l->cpu_freq);
	// printf("[wcet_stat] avg_alloc: %.1fns, avg_dealloc: %.1fns, alloc_idx: %lu, dealloc_idx: %lu\n", (double)avg_alloc * 1000 / (l->cpu_freq / FREQ_1M), (double)avg_dealloc * 1000 / (l->cpu_freq / FREQ_1M), l->sample_alloc_idx, l->sample_dealloc_idx);
	// printf("[wcet_stat] alloc cycles distribution :\n");
	//wcet_distribute(l->sample_alloc_slots, l->sample_alloc_idx, STEP, UPPER_LIMIT);
	// wcet_distribute_sum(snr_arr_alc, l->sample_alloc_slots, l->sample_alloc_idx, STEP, UPPER_LIMIT);

	// printf("[wcet_stat] dealloc cycles distribution :\n");
	//wcet_distribute(l->sample_dealloc_slots, l->sample_dealloc_idx, STEP, UPPER_LIMIT);
	// wcet_distribute_sum(snr_arr_dalc, l->sample_dealloc_slots, l->sample_dealloc_idx, STEP, UPPER_LIMIT);
	sum_arr(hd->sample_alloc_slots, l->sample_alloc_slots, MAX_SNR);
	sum_arr(hd->sample_dealloc_slots, l->sample_dealloc_slots, MAX_SNR);

	pthread_mutex_unlock(&mtx);
}


static __attribute__((destructor)) void wcet_final_sum_print()
{
	//if (l->sample_alloc_idx < MIN_REPORT_NR) return;

	/* 先全部相加 */
	/* 遍历链表 */
	wcet_obj_t *hd = wcethd;
	wcet_obj_t *cur = wcethd->next;
	while(cur){
		wcet_stat(hd, cur);
		cur = cur->next;
	}
	word_t sum = calculate_sum(hd->sample_alloc_slots, (UPPER_LIMIT / STEP) - 1);
	if (sum < MIN_REPORT_NR)
	{
		// printf("sum %lu\n", sum);
		return;
	}

	for (word_t i = 0; i < sizeof(percentiles) / sizeof(double); i++)
	{
		if(i != 0 )WCET_PRINT(" ");
		WCET_PRINT("%g", percentiles[i]);
	}
	WCET_PRINT("\n");

	// printf("----------------alloc-----------------\n");
	wcet_print(hd->sample_alloc_slots, STEP, UPPER_LIMIT / STEP);
	find_wcet_percentile(hd->sample_alloc_slots, percentiles, STEP, (UPPER_LIMIT / STEP), sizeof(percentiles) / sizeof(double));
	// printf("----------------dealloc-----------------\n");
	wcet_print(hd->sample_dealloc_slots, STEP, UPPER_LIMIT / STEP);
	find_wcet_percentile(hd->sample_dealloc_slots, percentiles, STEP, (UPPER_LIMIT / STEP), sizeof(percentiles) / sizeof(double));
}

